﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
using IcicleFramework.Components.Renderable;
using IcicleFramework.Renderables;
using IcicleFramework.Renderables.Components;
using Microsoft.Xna.Framework.Content;

namespace IcicleFramework.GameServices
{
    /// <summary>
    /// A Factory for generating IRenderable objects from XML definitions.
    /// </summary>
    public class RenderableFactory : GameService, IRenderableFactory
    {
        private ContentManager contentManager;

        public RenderableFactory(ContentManager content)
        {
            this.contentManager = content;
        }

        public Dictionary<string, IRenderable> GenerateRenderables(XElement element, IRenderComponent parent)
        {
            Dictionary<string, IRenderable> renderables = new Dictionary<string, IRenderable>();
            IEnumerable<XElement> renderableElements = element.Elements("renderable");

            int i = 0;

            foreach (XElement renderableElement in renderableElements)
            {
                IRenderable renderable = GenerateRenderable(renderableElement, parent);

                if (renderable != null)
                {
                    string name;

                    XAttribute nameAttrib = renderableElement.Attribute("name");
                    if (nameAttrib != null)
                        name = nameAttrib.Value;
                    else
                        name = i.ToString(CultureInfo.InvariantCulture);
                    
                    renderables.Add(name, renderable);

                    i++;
                }
            }
            
            return renderables;
        }

        public IRenderable GenerateRenderable(XElement element, IRenderComponent parent)
        {
            IRenderable renderable = null;

            XAttribute classAttrib = element.Attribute("class"), 
                        typeAttrib = element.Attribute("type");

            if (classAttrib != null && typeAttrib != null)
            {
                Type classType = Type.GetType(classAttrib.Value);

                renderable = (IRenderable) Activator.CreateInstance(classType);
                renderable.Deserialize(element);
                renderable.Load(contentManager);
                renderable.Visible = true;
                renderable.Parent = parent;

                XElement componentElem = element.Element("components");
                if (componentElem != null)
                {
                    List<XElement> componentElems = componentElem.Elements("component").ToList();

                    for (int i = 0; i < componentElems.Count; i++)
                    {
                        IRenderableComponent component = GenerateRenderableComponent(componentElems[i], renderable);

                        if (component != null)
                        {
                            component.Parent = renderable;
                            renderable.AddComponent(component.BaseType, component);
                        }
                    }
                }
            }
            return renderable;
        }

        protected IRenderableComponent GenerateRenderableComponent(XElement element, IRenderable renderable)
        {
            IRenderableComponent component = null;

            if (element.Attribute("class") != null && element.Attribute("type") != null)
            {
                Type classType = Type.GetType(element.Attribute("class").Value);
                string stringInterface = element.Attribute("type").Value;

                //Find the proper interface designated by the entity definition so we can store it properly in the IRenderable...
                Type interfaceType = Helper.FindInterface(classType, stringInterface);

                component = (IRenderableComponent) Activator.CreateInstance(classType);
                component.Deserialize(element);
                component.BaseType = interfaceType;
            }

            return component;
        }
    }
}
